package com.wyait.manage.idempotent.aop;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.wyait.manage.entity.ResponseResult;
import com.wyait.manage.idempotent.annotations.Idempotent;
import com.wyait.manage.idempotent.enums.IdempotentType;
import com.wyait.manage.idempotent.token.TokenManager;
import com.wyait.manage.utils.IStatusMessage;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * Package: com.wyait.manage.idempotent.aop
 * Description：防重复提交实现
 * Author: huangjian
 * Date:  2020/9/13 17:52
 * Modified By:
 * @author huangjian
 */
@Component
@Slf4j
public class IdempotentAdvice implements MethodInterceptor {

    private static final ObjectMapper mapper = new ObjectMapper();

    @Resource
    private TokenManager tokenManager;


    private void handlerReturn(Method currentMethod,
                               ServletRequestAttributes servletRequestAttributes,
                               IStatusMessage.SystemStatus systemStatus) {
        PrintWriter writer = null;
        try {
            HttpServletResponse response = servletRequestAttributes.getResponse();
            response.setCharacterEncoding("UTF-8");
            writer = response.getWriter();
            Class<?> returnType = currentMethod.getReturnType();
            Object returnValue;
            if (returnType.equals(String.class)) {
                response.setStatus(HttpServletResponse.SC_OK);
                response.setContentType(MediaType.ALL_VALUE);
                returnValue = systemStatus.getMessage();
                writer.write(returnValue.toString());
            } else{
                response.setStatus(HttpServletResponse.SC_OK);
                response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                if (returnType.equals(Map.class)) {
                    returnValue = new HashMap<>(1);
                    ((HashMap) returnValue).put("msg", systemStatus.getMessage());
                    writer.write(mapper.writeValueAsString(returnValue));
                } else {
                    returnValue = new ResponseResult();
                    ((ResponseResult) returnValue).setCode(systemStatus.getCode());
                    ((ResponseResult) returnValue).setMessage(systemStatus.getMessage());
                    writer.write(mapper.writeValueAsString(returnValue));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        writer.flush();
        writer.close();
    }

    @Override
    public Object invoke(MethodInvocation invocation) {
        Object proceed = null;
        try {
            log.info("拦截到了" + invocation.getMethod().getName() +"方法...");
            Method currentMethod =  invocation.getMethod();
            if (currentMethod.isAnnotationPresent(Idempotent.class)) {
                log.info(invocation.getThis().getClass().getName());
                //不加这个，会被执行两次，导致永远返回重复提交
                if(AopUtils.isAopProxy(invocation.getThis())){
                    ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                    HttpServletRequest request = servletRequestAttributes.getRequest();
                    Idempotent idempotent = currentMethod.getAnnotation(Idempotent.class);
                    String token;
                    if (idempotent.type() == IdempotentType.FORM) {
                        token = request.getParameter("X-Idempotent-Token");
                    } else {
                        token = request.getHeader("X-Idempotent-Token");
                    }
                    if (StringUtils.isEmpty(token)) {
                        this.handlerReturn(currentMethod, servletRequestAttributes, IStatusMessage.SystemStatus.INVALID_TOKEN_ERROR);
                        log.error(IStatusMessage.SystemStatus.INVALID_TOKEN_ERROR.getMessage());
                        return null;
                    }
                    if (!tokenManager.verifyToken(token)) {
                        this.handlerReturn(currentMethod, servletRequestAttributes, IStatusMessage.SystemStatus.IDEMPOTENT_ERROR);
                        log.error(IStatusMessage.SystemStatus.IDEMPOTENT_ERROR.getMessage().concat("->{}"),token);
                        return null;
                    }
                    proceed = invocation.proceed();
                }else{
                    proceed = invocation.proceed();
                }
            } else {
                proceed = invocation.proceed();
            }
            log.info("IdempotentNewAspect结束了！");
        } catch (Throwable e) {
            log.info("IdempotentNewAspect异常了！");
        }
        return proceed;
    }
}
